/**
 *******************************************************************************
 * @FileName  : tlsr825x_uart.h
 * @Author    : GaoQiu
 * @CreateDate: 2020-09-10
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */

#include "tlsr825x_uart.h"
#include "tlsr825x_systimer.h"

uint8_t txIdx = 0;
uint8_t rxIdx = 0;


/**
 * @brief     This function is used to look for the prime.if the prime is finded,it will
 * 			  return 1, or return 0.
 * @param[in] the value to judge
 * @return    none
 */
static uint8_t IsPrime(uint32_t n)
{
	uint32_t i = 5;
	if(n <= 3){
		return 1; //althought n is prime, but the bwpc must be larger than 2.
	}
	else if((n %2 == 0) || (n % 3 == 0)){
		return 0;
	}
	else{
		for(i=5;i*i<n;i+=6){
			if((n % i == 0)||(n %(i+2))==0){
				return 0;
			}
		}
		return 1;
	}
}

/**
 * @brief  		This funciton serves to calculate the best bwpc(bit width) .i.e reg0x96
 * @param[in] 	baut_rate:The value of the baut rate to set.
 * @param[in] 	tmp_sysclk:The system clock
 *    			algorithm: BaudRate*(div+1)*(bwpc+1)=system clock
 *    			simplify the expression: div*bwpc =  constant(z)
 *    			bwpc range from 3 to 15.so loop and
 *    			get the minimum one decimal point
 * @return 		the position of getting the minimum value
 */
static uint8_t  g_bwpc = 0;
static uint32_t g_uart_div = 0;
void GetBetterBwpc(uint32_t baut_rate, uint32_t tmp_sysclk)
{
	uint8_t i = 0, j= 0;
	uint32_t primeInt = 0;
	uint8_t primeDec = 0;
	uint32_t D_intdec[13],D_int[13];
	uint8_t D_dec[13];

	primeInt = tmp_sysclk/baut_rate;
	primeDec = 10*tmp_sysclk/baut_rate - 10*primeInt;
	/**
	 * calculate the primeInt and check whether primeInt is prime.
	 */
	if(IsPrime(primeInt)){ // primeInt is prime
		primeInt += 1;  //+1 must be not prime. and primeInt must be larger than 2.
	}
	else{
		if(primeDec > 5){ // >5
			primeInt += 1;
			if(IsPrime(primeInt)){
				primeInt -= 1;
			}
		}
	}
	/**
	 * get the best division value and bit width
	 */
	for(i=3;i<=15;i++){
		D_intdec[i-3] = (10*primeInt)/(i+1);////get the LSB
		D_dec[i-3] = D_intdec[i-3] - 10*(D_intdec[i-3]/10);///get the decimal section
		D_int[i-3] = D_intdec[i-3]/10;///get the integer section
	}

	//find the max and min one decimation point
	uint8_t position_min = 0,position_max = 0;
	uint32_t min = 0xffffffff,max = 0x00;
	for(j=0;j<13;j++){
		if((D_dec[j] <= min)&&(D_int[j] != 0x01)){
			min = D_dec[j];
			position_min = j;
		}
		if(D_dec[j]>=max){
			max = D_dec[j];
			position_max = j;
		}
	}

	if((D_dec[position_min]<5) && (D_dec[position_max]>=5)){
		if(D_dec[position_min]<(10-D_dec[position_max])){
			g_bwpc = position_min + 3;
			g_uart_div = D_int[position_min]-1;
		}
		else{
			g_bwpc = position_max + 3;
			g_uart_div = D_int[position_max];
		}
	}
	else if((D_dec[position_min]<5) && (D_dec[position_max]<5)){
		g_bwpc = position_min + 3;
		g_uart_div = D_int[position_min] - 1;
	}
	else{
		g_bwpc = position_max + 3;
		g_uart_div = D_int[position_max];
	}
}


/**
 * @berif : UART initialization.
 * @param : UART_InitStruct     UART initialization structure.
 * @return: None.
 */
void UART_Init(UART_InitType_t *UART_InitStruct)
{
	GetBetterBwpc(UART_InitStruct->UART_BaudRate, UART_InitStruct->SysClkFreq);

	UART->CLK_DIV = 0x8000 | (uint16_t)g_uart_div;
	UART->CTL0 = (g_bwpc & UART_CTRL0_BWPC);

	UART->CTL1 = UART_InitStruct->UART_StopBits | UART_InitStruct->UART_Parity;

	UART->TIMOUT  = (UART_Timeout_2x)|(((g_bwpc + 1) * 12) | UART_TIMEOUT);
	//UART->TIMOUT  = (UART_Timeout_3x)|(((g_bwpc + 1) * 12) | UART_TIMEOUT);

	UART->CTL0 &= ~(UART_IT_Rx | UART_IT_Tx);

	UART->CTL2 = 0x00;

	txIdx = 0;
	rxIdx = 0;
}

/**
 * @berif : UART Interrupt configuration.
 * @param : UART_IT     can be UART_IT_Rx/UART_IT_Rx/UART_IT_Rx_Tx.
 * @param : enable      enable/disable UART interrupt.
 * @return: None.
 */
void UART_ITConfig(uint8_t UART_IT, bool_t enable)
{
	if(enable){
		UART->CTL0 |= UART_IT;
	}
	else{
		UART->CTL0 &= ~UART_IT;
	}
	UART->CTL3 = 0x11; //Set Interrupt level.
}

/**
 * @berif : Get UART Interrupt state flag.
 * @param : UART_IT     can be UART_IT_Flag_RxDone/UART_IT_Flag_RxErr.
 * @return: None.
 */
uint8_t UART_GetITFlagState(uint8_t UART_IT_Flag)
{
	return UART->IRQ_STAT & UART_IT_Flag;
}

/**
 * @berif : Clear UART Interrupt state flag.
 * @param : UART_IT     can be UART_IT_Flag_RxDone/UART_IT_Flag_RxErr.
 * @return: None.
 */
void UART_ClearITFlagState(uint8_t UART_IT_Flag)
{
	UART->IRQ_STAT = UART_IT_Flag;
}

/**
 * @berif : UART send a byte.
 * @param : data       data will be sent.
 * @return: None.
 */
void UART_SendByte(uint8_t data)
{
	while((UART->BUF_CNT>>4) >=7){}
	uint8_t *pUartData = (uint8_t *)&UART->DAT0 + txIdx;
	*pUartData = data;
	txIdx++;
	txIdx &= 0x03;
}

/**
 * @berif : UART send data.
 * @param : pData       Pointer point to buffer.
 * @param : len         the length of pData.
 * @return: None.
 */
void UART_SendData(uint8_t *pData, uint32_t len)
{
	for(int i=0; i<len; i++)
	{
		while((UART->BUF_CNT>>4) >=7){}
		REG_8(0x90 + txIdx) = *pData++;
		txIdx++;
		txIdx &= 0x03;
	}
}

/**
 * @berif : UART receive data.
 * @param : None.
 * @return: return received data.
 */
uint8_t UART_ReceiveByte(void)
{
	uint8_t data = REG_8(0x90 + rxIdx);
	rxIdx++;
	rxIdx &= 0x03;
	return data;
}

/**
 * @berif : UART DMA mode configuration.
 * @param : pRxBuf        Pointer point to Rx buffer.
 * @param : byteLen       the length of Rx buffer.
 * @return: None.
 */
void UART_DmaConfig(uint8_t *pRxBuf, uint32_t byteLen)
{
	DMA->CHN0_ADDR = (uint16_t)(uint32_t)pRxBuf;
	DMA->CHN0_SIZE = (byteLen >> 4);
	DMA->CHN0_MODE = DMA_MODE_WRITE_MEM;
}

/**
 * @berif : UART DMA interrupt configuration.
 * @param : UART_DMA_IT      can be UART_DMA_IT_Rx/UART_DMA_IT_Tx/UART_DMA_IT_Rx_Tx
 * @param : enable           disable/enable DMA mode
 * @return: None.
 */
void UART_DmaITConfig(uint8_t UART_DMA_IT, bool_t enable)
{
	if(enable){
		DMA->IRQ_EN |= UART_DMA_IT;
	}
	else{
		DMA->IRQ_EN &= ~UART_DMA_IT;
	}
}

/**
 * @berif : UART DMA enable/disable configuration.
 * @param : UART_DMA      can be UART_DMA_Rx/UART_DMA_Tx/UART_DMA_Rx_Tx
 * @param : enable        enable/disable DMA
 * @return: None.
 */
void UART_DmaCmd(uint8_t UART_DMA, bool_t enable)
{
	if(enable)
	{
		UART->CTL0  |= UART_DMA;
		DMA->CHN_EN |= (uint8_t)(UART_DMA>>4);
	}
	else{
		UART->CTL0  &= ~UART_DMA;
		DMA->CHN_EN &= ~(uint8_t)(UART_DMA>>4);
	}
}

#if 0
/**
 * @berif : Get UART DMA interrupt state flag.
 * @param : UART_DMA_IT_Flag      can be UART_DMA_IT_Flag_RxDone/
 *                                       UART_DMA_IT_Flag_TxDone/
 *                                       UART_DMA_IT_Flag_RxTxDone
 * @return: None.
 */
RAM_CODE
uint8_t UART_GetDmaITFlagState(uint8_t UART_DMA_IT_Flag)
{
	return DMA->IRQ_STA & UART_DMA_IT_Flag;
}

/**
 * @berif : Clean UART DMA interrupt state flag.
 * @param : UART_DMA_IT_Flag      can be UART_DMA_IT_Flag_RxDone/
 *                                       UART_DMA_IT_Flag_TxDone/
 *                                       UART_DMA_IT_Flag_RxTxDone
 * @return: None.
 */
RAM_CODE
void UART_ClearDmaITFlagState(uint8_t UART_DMA_IT_Flag)
{
	DMA->IRQ_STA = UART_DMA_IT_Flag;
}
#endif

/**
 * @berif : UART send data by DMA.
 * @param : pData      Pointer point to data buffer.
 *          Note:      data format:
 *          LSB | data Length  |  data        | MSB
 *              |   4B         |  NBytes      |
 * @return: None.
 */
bool_t UART_SendDataByDma(uint8_t *pData)
{
#if 0
	//while(!(UART->STATUS1 & UART_STATE1_TX_DONE));
	if(!(UART->STATUS1 & UART_STATE1_TX_DONE)){
		return false;
	}
	DMA->CHN1_ADDR = (uint16_t)(uint32_t)pData;
	DMA->CHN1_SIZE = 0xff;
	DMA->CHN1_MODE &= ~DMA_MODE_WRITE_MEM;
	DMA->TX_TRIGGER |= (uint16_t)DMA_TIGG_UART_TX;
#else
	UART->STATUS2 = BIT(7);

	/* when system clock is 48M, delay must added. */
	delay_us(1);

	DMA->CHN1_ADDR = (uint16_t)(uint32_t)pData;
	DMA->CHN1_SIZE = 0xff;
	DMA->CHN1_MODE &= ~DMA_MODE_WRITE_MEM;
	DMA->TX_TRIGGER |= (uint16_t)DMA_TIGG_UART_TX;
#endif
	return true;
}

/**
 * @berif : Check if UART is busy.
 * @param : None.
 * @return: None.
 */
bool_t UART_IsBusy(void)
{
	return (UART->STATUS1 & UART_STATE1_TX_DONE) ? false:true;
}


#if 0
//TODO:algorithm: BaudRate*(div+1)*(bwpc+1)=system frequcy; 3 <= bwpc <= 16;
void CalculateBwpc(void)
{
	uint32_t div  = 0;
	uint8_t  bwpc = 3;

	uint32_t div1 = div = ROUNDED_DIV(UART_InitStruct->SysClkFreq * 10, UART_InitStruct->UART_BaudRate * (bwpc+1));
	uint32_t tmp = div % 10;
	uint32_t tmp1 = 0;

	for(int i=4; i<16; i++)
	{
		div1 = ROUNDED_DIV(UART_InitStruct->SysClkFreq * 10, UART_InitStruct->UART_BaudRate * (i+1));
		tmp1 = div1 % 10;

		if(tmp1 < tmp){
			tmp = tmp1;
			div = div1;
			bwpc = i;
		}
	}

	div = ROUNDED_DIV(div, 10) + 1;
}
#endif
